﻿/*
   Open Intel 
   Copyright © 2011 – ISC. All Rights Reserved.
*/

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Windows.Controls;
using Caliburn.Micro;
using OI;
using OI.Async;
using OI.DataModel;
using OI.Framework;
using OIManager.Async;
using OIManager.Events;
using OIManager.Views;


namespace OIManager.ViewModels
{
    [Export("SetupCatalogViewModel", typeof(SetupCatalogViewModel))]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public class SetupCatalogViewModel : Screen, IHandle<ActivateJurisdiction>
    {
        #region DATA
        readonly IEventAggregator Events;
        readonly IWindowManager WindowManager;
        public DataForm DetailsForm { get; private set; }
        public bool UpdatingDataset { get; set; }


        public BindableCollection<OICategory> CategoryItems
        {
            get 
            { 
                return _CategoryItems ?? (_CategoryItems = new BindableCollection<OICategory>()); 
            }
        }
        private BindableCollection<OICategory> _CategoryItems;


        public BindableCollection<OISource> SourceItems
        {
            get
            {
                return _SourceItems ?? (_SourceItems = new BindableCollection<OISource>());
            }
        }
        private BindableCollection<OISource> _SourceItems;


        public BindableCollection<OIDataset> Items
        {
            get 
            { 
                return _Items ?? (_Items = new BindableCollection<OIDataset>()); 
            }
        }
        private BindableCollection<OIDataset> _Items;


        // selected item in catalog
        public OIDataset Item
        {
            get
            {
                return _Item;
            }
            set
            {
                _Item = value;

                // ensure referential integrity to categories and sources
                if (_Item != null &&
                    _Item.CategoryID == 0 && 
                    CategoryItems.Count > 0)
                {
                    _Item.CategoryID = CategoryItems[0].ID;
                }
                if (_Item != null &&
                    _Item.SourceID == 0 && 
                    SourceItems.Count > 0)
                {
                    _Item.SourceID = SourceItems[0].ID;
                }

                // only update UI on change
                if (_Item != null)
                {
                    NotifyOfPropertyChange(() => Item);
                    NotifyOfPropertyChange(() => CanRemDataset);
                    NotifyOfPropertyChange(() => SelectedCategoryValue);
                    NotifyOfPropertyChange(() => SelectedSourceValue);
                }
            }
        }
        private OIDataset _Item;


        // bound to selected value in category combobox
        public int SelectedCategoryValue
        {
            get
            {
                if (Item == null || CategoryItems == null)
                {
                    return -1;
                }

                return Item.CategoryID;
            }
            set
            {
                Item.CategoryID = value;
                NotifyOfPropertyChange(() => SelectedCategoryValue);
            }
        }


        // bound to selected value in source combobox
        public int SelectedSourceValue
        {
            get
            {
                if (Item == null || SourceItems == null)
                {
                    return -1;
                }

                return Item.SourceID;
            }
            set
            {
                Item.SourceID = value;
                NotifyOfPropertyChange(() => SelectedSourceValue);
            }
        }


        // by convention
        public bool CanRemDataset
        {
            get
            {
                return Item != null && Items.Count > 0;
            }
        }


        // by convention
        public bool CanAddDataset
        {
            get { return _CanAddDataset; }
            set 
            { 
                _CanAddDataset = value;
                NotifyOfPropertyChange(() => CanAddDataset);
            }
        }
        private bool _CanAddDataset;
        #endregion


        [ImportingConstructor]
        public SetupCatalogViewModel(IEventAggregator events, IWindowManager wm)
		{
            Events = events;
			events.Subscribe(this);
            WindowManager = wm;
		}


        protected override void OnViewLoaded(object view)
        {
            base.OnViewLoaded(view);

            // get the details DataForm and wire up some event handlers
            if (DetailsForm == null)
            {
                DetailsForm = ((SetupCatalogView)view).DetailsForm;
                DetailsForm.BeginningEdit += DetailsFormBeginningEdit;
                DetailsForm.EditEnded += DetailsFormEditEnded;
            }

            if (Items.Count == 0)
            {
                Coroutine.BeginExecute(Setup());
            }
        }


        protected override void OnDeactivate(bool close)
        {
            base.OnDeactivate(close);
            DetailsForm.CurrentIndex = -1;
            Item = null;
        }


         // setup application
        public IEnumerator<IResult> Setup()
        {
            IoC.Get<IShell>().Busy(true, "loading catalog...");

            // cats
            var cq = new CatalogQuery<OICategory>(Configuration.Jurisdiction.ID);
            yield return cq;
            CategoryItems.Clear();
            CategoryItems.AddRange(cq.Results);

            // sources
            var sq = new CatalogQuery<OISource>(Configuration.Jurisdiction.ID);
            yield return sq;
            SourceItems.Clear();
            SourceItems.AddRange(sq.Results);

            // datasets
            var dsq = new CatalogQuery<OIDataset>(Configuration.Jurisdiction.ID) { ShowUnpublished = true };
            yield return dsq;
            Items.Clear();
            Items.AddRange(dsq.Results);
            CanAddDataset = true;

            IoC.Get<IShell>().Busy(false);
        }


        public void Handle(ActivateJurisdiction message)
        {
            CategoryItems.Clear();
            SourceItems.Clear();
            Items.Clear();
        }


        // DataForm edit begin
        private void DetailsFormBeginningEdit(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (Item != null)
            {
                // set validation function
                Item.PostValidationFunction = null;

                // set post cancel action used to refresh UI from undo checkpoint
                Item.PostCancelAction = () =>
                {
                    NotifyOfPropertyChange(() => SelectedCategoryValue);
                    NotifyOfPropertyChange(() => SelectedSourceValue);
                };
            }
        }


        // DataForm edit ended
        private void DetailsFormEditEnded(object sender, DataFormEditEndedEventArgs e)
        {
            if (e.EditAction == DataFormEditAction.Commit && !UpdatingDataset)
            {
                 Coroutine.BeginExecute(SetDatasetAsync(Item));
            }
            CanAddDataset = true;
        }

        // -------------------------------------------------------------------


        // coroutine for async update of a dataset
        public IEnumerator<IResult> SetDatasetAsync(OIDataset ds)
        {
            IoC.Get<IShell>().Busy(true, "updating dataset...");
            UpdatingDataset = true;

            var svm =  IoC.Get<SettingsViewModel>();
            var cu = new CatalogUpdate(ds, svm.SQLConnection);
            yield return cu;

            if (!string.IsNullOrEmpty(cu.Results))
            {
                IoC.Get<IShell>().HandleException(new Exception(cu.Results), "Update dataset failed");
            }
            else
            {
                // reload to get new ID
                var dsq = new CatalogQuery<OIDataset>(Configuration.Jurisdiction.ID) { ShowUnpublished = true };
                yield return dsq;
                foreach (var item in dsq.Results)
                {
                    if (item.Name == ds.Name)
                    {
                        ds.ID = item.ID;
                        break;
                    }
                }
                Items.Refresh();
            }

            UpdatingDataset = false;
            IoC.Get<IShell>().Busy(false);
        }


        // coroutine for async delete of a dataset
        public IEnumerator<IResult> RemoveDatasetAsync(int dsID)
        {
            IoC.Get<IShell>().Busy(true, "deleting dataset...");

            var svm = IoC.Get<SettingsViewModel>();
            var cd = new CatalogRemove(dsID, svm.SQLConnection);
            yield return cd;

            IoC.Get<IShell>().Busy(false);

            if (!string.IsNullOrEmpty(cd.Results))
            {
                IoC.Get<IShell>().HandleException(new Exception(cd.Results), "Delete dataset failed");
            }
            else
            {
                DetailsForm.DeleteItem();
            }

            NotifyOfPropertyChange(() => CanRemDataset);
        }
       

        // by convention
        public void AddDataset()
        {
            // this is a hack over AddNewItem that fixes consecutive add problem
            var newItem = new OIDataset();
            Items.Add(newItem);
            Item = newItem;
            DetailsForm.BeginEdit();
            DetailsForm.CancelEdit();
            DetailsForm.BeginEdit();

            // make sure new record has jurisdiction FK (since this is not editable)
            Item.JurisdictionFK = Configuration.Jurisdiction.ID;

            // this is a new item, add a validation action to make sure added item doesn't use the same dataset name
            var names = Items.Select(i => i.Name).ToList();
            Item.PostValidationFunction = (ds, colName) =>
            {
                if (colName == "Name")
                {
                    if (names.Any(n => n.ToLower() == ds.Name.ToLower()))
                    {
                        return "is already in use, please select a different name";
                    }
                }
                return null;
            };

            CanAddDataset = false;
        }


        // by convention
        public void RemDataset()
        {
            if (Item != null)
            {
                System.Action remAction = () =>  Coroutine.BeginExecute(RemoveDatasetAsync(Item.ID));

                var dialog = new MessageBoxViewModel(true, remAction)
                {
                    DisplayName = "Remove Dataset?",
                    Message = "Are you sure you want to remove " + Item.Name + " ?"
                };

                IoC.Get<IWindowManager>().ShowDialog(dialog);
            }
        }

        // --------------------------------------------------------------------

        // by convention
        public void AddCategory()
        {
            Action<string> addAction = (name) =>
            {
                if (CategoryItems.FirstOrDefault(ci => ci.Name == name) == null)
                {
                    var oiCat = new OICategory()
                    {
                        JurisdictionFK = Configuration.Jurisdiction.ID,
                        Name = name
                    };
                     Coroutine.BeginExecute(AddCategoryAsync(oiCat));
                }
            };

            WindowManager.ShowDialog(new AddCategoryViewModel(addAction));
        }


        public IEnumerator<IResult> AddCategoryAsync(OICategory oiCat)
        {
            var svm = IoC.Get<SettingsViewModel>();
            var ca = new CategoryAdd(oiCat, svm.SQLConnection);
            yield return ca;

            // now that we added a new category, reload them to get PK
            var cq = new CatalogQuery<OICategory>(Configuration.Jurisdiction.ID);
            yield return cq;
            foreach (var item in cq.Results)
            {
                if (item.Name == oiCat.Name)
                {
                    oiCat.ID = item.ID;
                    break;
                }
            }
            CategoryItems.Add(oiCat);
            SelectedCategoryValue = oiCat.ID;
        }


        // by convention
        public void RemCategory()
        {
            if (CategoryItems.Count > 0 && SelectedCategoryValue > 0)
            {
                var cat = CategoryItems.FirstOrDefault(i => i.ID == SelectedCategoryValue);
                if (cat != null)
                {
                    System.Action remAction = () =>  Coroutine.BeginExecute(RemCategoryAsync(cat));

                    var dialog = new MessageBoxViewModel(true, remAction)
                    {
                        DisplayName = "Remove Category?",
                        Message = "Are you sure you want to remove " + cat.Name + " ?"
                    };

                    IoC.Get<IWindowManager>().ShowDialog(dialog);
                }
            }
        }


        public IEnumerator<IResult> RemCategoryAsync(OICategory cat)
        {
            var svm = IoC.Get<SettingsViewModel>();
            var cr = new CategoryRemove(cat.ID, svm.SQLConnection);
            yield return cr;

            // the result holds an error string - if any
            if (!string.IsNullOrEmpty(cr.Results))
            {
                IoC.Get<IShell>().HandleException(null, cr.Results);
            }
            else
            {
                // remove the category from UI
                CategoryItems.Remove(cat);
                if (CategoryItems.Count > 0)
                {
                    SelectedCategoryValue = CategoryItems[0].ID;
                }
            }
        }

        // --------------------------------------------------------------------

        // by convention
        public void AddSource()
        {
            Action<string, string> addAction = (name, filter) =>
            {
                if (SourceItems.FirstOrDefault(si => si.Name == name) == null)
                {
                    var oiSrc = new OISource()
                    {
                        JurisdictionFK = Configuration.Jurisdiction.ID,
                        Name = name,
                        Filter = filter
                    };
                     Coroutine.BeginExecute(AddSourceAsync(oiSrc));
                }
            };

            WindowManager.ShowDialog(new AddSourceViewModel(addAction));
        }


        public IEnumerator<IResult> AddSourceAsync(OISource oiSrc)
        {
            var svm = IoC.Get<SettingsViewModel>();
            var sa = new SourceAdd(oiSrc, svm.SQLConnection);
            yield return sa;

            // now that we added a new source, reload them to get PK
            var cq = new CatalogQuery<OISource>(Configuration.Jurisdiction.ID);
            yield return cq;
            foreach (var item in cq.Results)
            {
                if (item.Name == oiSrc.Name)
                {
                    oiSrc.ID = item.ID;
                    break;
                }
            }
            SourceItems.Add(oiSrc);
            SelectedSourceValue = oiSrc.ID;
        }


        // by convention
        public void RemSource()
        {
            if (SourceItems.Count > 0 && SelectedSourceValue > 0)
            {
                var src = SourceItems.FirstOrDefault(i => i.ID == SelectedSourceValue);
                if (src != null)
                {
                    System.Action remAction = () =>  Coroutine.BeginExecute(RemSourceAsync(src));

                    var dialog = new MessageBoxViewModel(true, remAction)
                    {
                        DisplayName = "Remove Source?",
                        Message = "Are you sure you want to remove " + src.Name + " ?"
                    };

                    IoC.Get<IWindowManager>().ShowDialog(dialog);
                }
            }
        }


        public IEnumerator<IResult> RemSourceAsync(OISource src)
        {
            var svm = IoC.Get<SettingsViewModel>();
            var sr = new SourceRemove(src.ID, svm.SQLConnection);
            yield return sr;

            // the result holds an error string - if any
            if (!string.IsNullOrEmpty(sr.Results))
            {
                IoC.Get<IShell>().HandleException(null, sr.Results);
            }
            else
            {
                // remove the source from UI
                SourceItems.Remove(src);
                if (SourceItems.Count > 0)
                {
                    SelectedSourceValue = SourceItems[0].ID;
                }
            }
        }
    }
}
